﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SuperQuiz.Model;
using SuperQuiz.View;
using SuperQuiz.Utils;
using TCD.Windows8.Controls;
using Windows.Storage.Search;
using Windows.UI;
using Windows.UI.ApplicationSettings;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.ApplicationModel.Resources;
using System.Reflection;

namespace SuperQuiz.Logic
{
    public class QuizApplication : BindableBase
    {
        private static QuizApplication _Instance;

        public static QuizApplication Instance
        {
            get 
            {
                if (_Instance == null)
                {
                    var assembly = typeof(QuizApplication).GetTypeInfo().Assembly;
                    var applicationType = assembly.DefinedTypes.Where((t) => t.BaseType.Equals(typeof(QuizApplication))).FirstOrDefault();
                    if (applicationType == null)
                    {
                        applicationType = typeof(QuizApplication).GetTypeInfo();
                    }
                    _Instance = (QuizApplication)Activator.CreateInstance(applicationType.AsType());
                }
                return _Instance; 
            }
        }

        private ObservableCollection<QuizCategory> _quizCategories = new ObservableCollection<QuizCategory>();
        public ObservableCollection<QuizCategory> QuizCategories
        {
            get { return this._quizCategories; }
        }

        protected Dictionary<string, QuizCategory> CategoryDictionary = new Dictionary<string, QuizCategory>();
        protected Dictionary<string, Quiz> QuizDictionary = new Dictionary<string, Quiz>();
        protected Dictionary<string, QuizPlay> QuizPlayDictionary = new Dictionary<string, QuizPlay>();
        protected Dictionary<string, List<QuizScore>> QuizScoreDictionary = new Dictionary<string, List<QuizScore>>();

        private Task LoadTask;

        public async Task LoadAsync()
        {
            if (LoadTask == null)
            {
                LoadTask = InternalLoad();
            }
            await LoadTask;
        }

        public virtual void Initialize()
        {
        }

        private async Task InternalLoad()
        {
            LoadSettingsAsync();

            QuizCategories.Clear();
            CategoryDictionary.Clear();
            QuizDictionary.Clear();

            await LoadQuizzes();

            var scores = await ObjectPersister.LoadAsync<Dictionary<string, List<QuizScore>>>("QuizScore", ObjectPersisterLocation.Roaming);
            if (scores != null)
            {
                QuizScoreDictionary = scores;
            }
        }

        protected virtual async Task LoadQuizzes()
        {
            await LoadCategoriesAndQuizzes();
            await GenerateSampleCategoriesAndQuizzes();
        }

        private async Task GenerateSampleCategoriesAndQuizzes()
        {
            var categories = await QuizDataSource.GenerateSampleQuizzes();
            foreach (var category in categories)
            {
                QuizCategories.Add(category);
                CategoryDictionary[category.UniqueId] = category;
                foreach (var quiz in category.Quizzes)
                {
                    QuizDictionary[quiz.UniqueId] = quiz;
                    foreach (var question in quiz.Questions)
                    {
                        question.Quiz = quiz;
                    }
                }
            }
        }

        private async Task LoadCategoriesAndQuizzes()
        {
            var appCategoriesFolder = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFolderAsync(@"SuperQuiz\Assets\Categories");
            var quizCategoriesQuery = appCategoriesFolder.CreateFileQueryWithOptions(new QueryOptions(CommonFileQuery.OrderByName, new string[] { ".xml" }));
            foreach (var item in await quizCategoriesQuery.GetFilesAsync())
            {
                try
                {
                    var category = await ObjectPersister.LoadAsync<QuizCategory>(item.Path, ObjectPersisterLocation.Path);
                    if (category.Quizzes == null)
                    {
                        category.Quizzes = new ObservableCollection<Quiz>();
                    }
                    CategoryDictionary[category.UniqueId] = category;
                    QuizCategories.Add(category);
                }
                catch { }
            }

            var appQuizzesFolder = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFolderAsync(@"SuperQuiz\Assets\Quizzes");
            var quizFilesQuery = appQuizzesFolder.CreateFileQueryWithOptions(new QueryOptions(CommonFileQuery.OrderByName, new string[] { ".xml" }));
            foreach (var item in await quizFilesQuery.GetFilesAsync())
            {
                try
                {
                    var quiz = await ObjectPersister.LoadAsync<Quiz>(item.Path, ObjectPersisterLocation.Path);
                    foreach (var question in quiz.Questions)
                    {
                        question.Quiz = quiz;
                    }
                    QuizDictionary[quiz.UniqueId] = quiz;
                    if (CategoryDictionary.ContainsKey(quiz.CategoryUniqueId))
                    {
                        CategoryDictionary[quiz.CategoryUniqueId].Quizzes.Add(quiz);
                    }
                }
                catch { }
            }

            foreach (var category in QuizCategories.ToList())
            {
                if (category.Quizzes.Count == 0)
                {
                    QuizCategories.Remove(category);
                    CategoryDictionary.Remove(category.UniqueId);
                }
            }
        }

        private ObservableCollection<QuizCategory> _SearchResult = new ObservableCollection<QuizCategory>();
        public ObservableCollection<QuizCategory> SearchResults
        {
            get { return this._SearchResult; }
        }

        private QuizSettings _Settings = new QuizSettings();
        public QuizSettings Settings
        {
            get { return this._Settings; }
            set { this.SetProperty(ref this._Settings, value); }
        }

        public QuizCategory GetCategory(string uniqueId)
        {
            QuizCategory category = null;
            CategoryDictionary.TryGetValue(uniqueId, out category);
            return category;
        }

        public Quiz GetQuiz(string uniqueId)
        {
            Quiz quiz = null;
            QuizDictionary.TryGetValue(uniqueId, out quiz);
            return quiz;
        }

        protected virtual QuizPlay CreateQuizPlay()
        {
            return new QuizPlay();
        }

        public QuizPlay StartQuiz(Quiz quiz)
        {
            var quizPlay = CreateQuizPlay();
            quizPlay.Quiz = quiz;
            quizPlay.DateStarted = DateTime.Now;
            quizPlay.UniqueId = Guid.NewGuid();

            foreach (var question in quiz.Questions)
            {
                quizPlay.SelectedAnswerIndexes.Add(-1);
                quizPlay.QuestionsLocked.Add(false);
            }
            quizPlay.CurrentQuestionIndex = 0;
            QuizPlayDictionary[quiz.UniqueId] = quizPlay;
            return quizPlay;
        }

        public QuizPlay GetQuizPlay(string quizUniqueId)
        {
            QuizPlay quizPlay = null;
            QuizPlayDictionary.TryGetValue(quizUniqueId, out quizPlay);
            return quizPlay;
        }

        public async Task AwaitLoad()
        {
            await LoadTask;
        }

        public async Task SaveAsync()
        {
            await ObjectPersister.SaveAsync("QuizPlay", ObjectPersisterLocation.Roaming, QuizPlayDictionary.Values.ToList());
        }

        public async Task RestoreAsync()
        {
            await AwaitLoad();
            var quizPlays = await ObjectPersister.LoadAsync<List<QuizPlay>>("QuizPlay", ObjectPersisterLocation.Roaming);
            if (quizPlays != null)
            {
                QuizPlayDictionary.Clear();
                foreach (var quizPlay in quizPlays)
                {
                    QuizPlayDictionary[quizPlay.Quiz.UniqueId] = quizPlay;
                    if (quizPlay.Results != null)
                    {
                        quizPlay.Results.Quiz = quizPlay.Quiz;
                        foreach (var item in quizPlay.Results.Items)
                        {
                            item.Question = quizPlay.Quiz.Questions[item.QuestionIndex];
                        }
                    }
                }
            }
        }

        public async Task SaveScore(QuizScore quizScore)
        {
            if (!QuizScoreDictionary.ContainsKey(quizScore.QuizUniqueId))
            {
                QuizScoreDictionary[quizScore.QuizUniqueId] = new List<QuizScore>();
            }
            QuizScoreDictionary[quizScore.QuizUniqueId].Add(quizScore);
            await ObjectPersister.SaveAsync("QuizScore", ObjectPersisterLocation.Roaming, QuizScoreDictionary);
        }

        public string GetLastPlayerName(string quizUniqueId)
        {
            if (QuizScoreDictionary.ContainsKey(quizUniqueId))
            {
                return QuizScoreDictionary[quizUniqueId].OrderByDescending((s) => s.DatePlayed).Select((s) => s.PlayerName).FirstOrDefault();
            }
            return String.Empty;
        }

        public void SetupSettingsEvents(Page page)
        {
            page.Loaded += page_Loaded;
            page.Unloaded += page_Unloaded;
        }

        void page_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
        {
            SettingsPane.GetForCurrentView().CommandsRequested += CommandsRequested;
        }

        void page_Unloaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
        {
            var page = (Page)sender;
            page.Loaded -= page_Loaded;
            page.Unloaded -= page_Unloaded;
            SettingsPane.GetForCurrentView().CommandsRequested -= CommandsRequested;
        }

        void CommandsRequested(SettingsPane sender, SettingsPaneCommandsRequestedEventArgs args)
        {
            var resourceLoader = new ResourceLoader();

            SettingsCommand generalCommand = new SettingsCommand("generalSettings", 
                resourceLoader.GetString("GeneralSettingsTitle"), 
                new UICommandInvokedHandler(onSettingsCommand));
            args.Request.ApplicationCommands.Add(generalCommand);

            SettingsCommand aboutCommand = new SettingsCommand("about", 
                resourceLoader.GetString("AboutTitle"), 
                new UICommandInvokedHandler(onAboutCommand));
            args.Request.ApplicationCommands.Add(aboutCommand);
        }

        static void onAboutCommand(IUICommand command)
        {
            var resourceLoader = new ResourceLoader();

            SettingsCommand settingsCommand = (SettingsCommand)command;
            Flyout f = new Flyout(
                new SolidColorBrush(Colors.Black),
                new SolidColorBrush(Colors.BlanchedAlmond),
                resourceLoader.GetString("AboutTitle"),
                FlyoutDimension.Narrow,
                new View.AboutControl());
            f.Show();
        }

        void onSettingsCommand(IUICommand command)
        {
            var resourceLoader = new ResourceLoader();

            SettingsCommand settingsCommand = (SettingsCommand)command;
            Flyout f = new Flyout(
                new SolidColorBrush(Colors.Black),
                new SolidColorBrush(Colors.BlanchedAlmond),
                resourceLoader.GetString("GeneralSettingsTitle"),
                FlyoutDimension.Narrow,
                new GeneralSettingsControl());
            f.Show();
        }

        public async Task SaveSettingsAsync()
        {
            await ObjectPersister.SaveAsync("QuizSettings", ObjectPersisterLocation.Roaming, Settings);
        }

        public async Task LoadSettingsAsync()
        {
            var settings = await ObjectPersister.LoadAsync<QuizSettings>("QuizSettings", ObjectPersisterLocation.Roaming);
            if (settings != null) 
            {
                Settings = settings;
            }
        }

        private bool ContainsWord(string text, string searchTerm)
        {
            if (text == null)
                return false;
            return text.IndexOf(searchTerm, StringComparison.CurrentCultureIgnoreCase) > -1;
        }

        public async Task Search(string search)
        {
            SearchResults.Clear();

            ObservableCollection<QuizCategory> categories = QuizApplication.Instance.QuizCategories;
            foreach (QuizCategory category in categories)
            {
                QuizCategory searchCategory = null;
                bool found;

                foreach (Quiz quiz in category.Quizzes)
                {
                    found = false;

                    if (ContainsWord(quiz.Name, search) || ContainsWord(quiz.Description, search))
                    {
                        found = true;
                    }
                    else
                    {
                        foreach (QuizQuestion question in quiz.Questions)
                        {
                            if (!found)
                            {
                                if (ContainsWord(question.Title, search)
                                    || ContainsWord(question.SubTitle, search)
                                    || ContainsWord(question.Description, search))
                                {
                                    found = true;
                                    break;
                                }
                                else
                                {
                                    if (!found)
                                    {
                                        foreach (QuizAnswer answer in question.Answers)
                                        {
                                            if (ContainsWord(answer.Name, search)
                                                || ContainsWord(answer.Description, search))
                                            {
                                                found = true;
                                                break;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }

                    if (found)
                    {
                        if (searchCategory == null)
                        {
                            searchCategory = new QuizCategory();
                            searchCategory.UniqueId = category.UniqueId;
                            searchCategory.Name = category.Name;
                            searchCategory.Quizzes = new ObservableCollection<Quiz>();
                        }
                        searchCategory.Quizzes.Add(quiz);
                    }

                }
                if (searchCategory != null)
                {
                    SearchResults.Add(searchCategory);
                }
            }
        }

        public Type HomePageType { get; set; }

        public string HomePageParameter { get; set; }
    }
}
